package com.allen.app.common.utils;

import cn.hutool.core.util.StrUtil;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.Arrays;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.ObjectUtils;

public final class Exceptions {

    private static AntPathMatcher matcher = new AntPathMatcher();

    /**
     * @deprecated {@link Try#fun(Try.UncheckedFunction)}
     */
    @Deprecated
    public static <T, R> Function<T, R> tryFun(Try.UncheckedFunction<T, R> mapper) {
        return Try.fun(mapper);
    }

    /**
     * @deprecated {@link Try#bool(Try.UncheckedPredicate)}
     */
    @Deprecated
    public static <T> Predicate<T> tryBool(Try.UncheckedPredicate<T> mapper) {
        return Try.bool(mapper);
    }

    public static Throwable unwrap(final Throwable t) {
        if (t instanceof InvocationTargetException) {
            return unwrap(((InvocationTargetException) t).getTargetException());
        }
        if (t instanceof UndeclaredThrowableException) {
            return unwrap(((UndeclaredThrowableException) t).getUndeclaredThrowable());
        }
        return t;
    }

    public static Set<String> stackTrace(final Throwable t, final String... basePackages) {
        Set<String> stackTraceList = Arrays.stream(t.getStackTrace())
                .filter(element -> matcher(element.getClassName(), basePackages) && element.getLineNumber() > 0 && Objects.nonNull(element.getMethodName()))
                .map(s -> StrUtil.format("at {} = {} ({} : {})", s.getClassName(), s.getMethodName(), s.getFileName(), s.getLineNumber()))
                .collect(Collectors.toSet());
        Throwable cause = t.getCause();
        if (cause != null) {
            Set<String> list = stackTrace(cause, basePackages);
            stackTraceList.addAll(list);
        }
        return stackTraceList;
    }

    public static void report(String msg, Throwable t) {
        System.err.println(msg);
        System.err.println("Reported exception:");
        t.printStackTrace();
    }

    private static boolean matcher(String currentPackage, String... basePackages) {
        if (!ObjectUtils.isEmpty(basePackages)) {
            for (String basePackage : basePackages) {
                if (matcher.match(basePackage, currentPackage)) {
                    return true;
                }
            }
        }
        return false;
    }
}
